#!/usr/bin/perl -w
use strict;
use warnings;
use FindBin qw/$Bin/;
use lib "$Bin/lib", "$Bin/../lib";
use Data::Dumper qw(Dumper);
use Farsail;
use Getopt::Long;
use Curses;

$ENV{FARSAILDIR} ||= "$Bin/../conf";
my ($batch, $procid);
GetOptions(
    'batch' => \$batch,
    'procid' => \$procid,
);

my $farsail = Farsail->instance;
my $actions = $farsail->actions;
my $dbh = $farsail->db('dod');
my $ev_table = 'farsail.run_event';

my $sql = <<SQL;
SELECT action, event, event_time::time
  FROM $ev_table
WHERE event_time >= ? AND procid = ?
      AND action != 'FARSAIL'
      AND event in ('START', 'END', 'SKIP', 'ERROR')
SQL
my $ev_sth = $dbh->prepare($sql);

if ( $batch ) {
    print get_status();
} else {
    mainloop();
}

sub mainloop {
    my $timeout = 5;
    initscr();                  # start screen
    noecho();
    cbreak();
    nodelay(1);                 # so getch() is non.blocking
    $SIG{INT} = sub { done("Ouch!") };

    while (1) {
        while ( ( my $key = getch() ) ne ERR ) { # maybe multiple keys
            done("See ya") if $key eq 'q';
        }
        my @data = split /\n/, get_status();
        for ( my $i = 0; $i < $LINES; $i++ ) {
            $data[$i] ||= '';
            addstr( $i, 0, $data[$i] . ' ' x ($COLS -length($data[$i])) );
        }
        standout();
        addstr( $LINES . 1, $COLS . 24, scalar localtime );
        standend();
        move( 0, 0 );
        refresh();              # flush new output to display
        my ( $in, $out ) = ( '', '' );
        vec( $in, fileno(STDIN), 1 ) = 1; # look for key on stdin
        select( $out = $in, undef, undef, $timeout ); # wait up to this long
    }
}

sub done {
    endwin();
    exit;
}

sub get_status{
    my $events = get_events();
    my $action = $events->[0][0];
    my $tree = {};
    calc_depends($tree, $action, {}, $events, 0);
    my $content = sprintf(
        "%-40s %-10s %-8s %-8s\n",
        'ACTION', 'STATUS', 'START', 'END'
    );
    $content .= dump_tree($action, $tree, 0);
    return $content;
}

sub get_events {
    my @bind_params;
    my $sql = <<SQL;
SELECT event_time, procid 
  FROM $ev_table
 WHERE action='FARSAIL'
   AND event='START'
SQL
    if ( $procid ) {
        $sql .= "and procid = $procid\n";
        push @bind_params, $procid;
    }
    $sql .= <<SQL;
ORDER BY event_time DESC
 LIMIT 1;
SQL
    my $sth = $dbh->prepare($sql);
    $sth->execute( @bind_params );
    my $result = $sth->fetchall_arrayref();
    unless ( @$result ) {
        die "Seem no actions have run before!\n";
    }
    my ($start_time, $procid) = @{$result->[0]};
    $ev_sth->execute($start_time, $procid);
    return $ev_sth->fetchall_arrayref;
    # my $events = do("events.pm");
    # return $events;
}

sub dump_tree {
    my ($action, $tree, $depth) = @_;
    my $content = sprintf(
        "%-40s %-10s %s %s\n",
        ('  ' x $depth) . $action,
        ($tree->{status} || 'WAIT'),
        ($tree->{start_time}||''),
        ($tree->{end_time} ||'')
    );
    for ( @{$tree->{depends}} ) {
        $content .= dump_tree( $_->[0], $_->[1], $depth+1);
    }
    return $content;
}

sub calc_depends {
    my ($tree, $action, $completed, $runinfo, $has_runing) = @_;
    if ( !ref $action ) {
        $action = $actions->action($action, '', 1);
    }
    my $name = $action->full_name;
    if ( exists $completed->{$name} ) {
        $tree->{status} = 'Seen';
        return $has_runing;
    }
    # check start
    if ( @$runinfo ) {
        my $info = shift @$runinfo;
        if ( $name ne $info->[0] ) {
            die "Not match info: @$info ($name)\n";
        }
        if ( lc($info->[1]) ne 'start' ) {
            die "Not match info: @$info ($name start)\n";
        }
        $tree->{start_time} = $info->[2];
    }
    $completed->{$name}++;
    # check skip
    if ( @$runinfo ) {
        if ( lc($runinfo->[0][1]) eq 'skip' ) {
            shift @$runinfo;
            $tree->{status} = "Skip";
            return $has_runing;
        }
    }
    # run depends
    for ( $action->depends ) {
        my $subtree = {};
        $has_runing = calc_depends($subtree, $_, $completed, $runinfo, $has_runing);
        push @{$tree->{depends}}, [ $_, $subtree ];
    }
    # check end
    if ( @$runinfo ) {
        my $info = shift @$runinfo;
        if ( $name ne $info->[0] ) {
            die "Not match info: @$info ($name)\n";
        }
        if ( lc($info->[1]) eq 'end' ) {
            $tree->{status} = 'Finish';
        } elsif ( lc($info->[1]) eq 'error' ) {
            $tree->{status} = 'Error';
        } else {
            die "Not match info: @$info ($name end)\n";
        }
        $tree->{end_time} = $info->[2];
    } elsif ( $has_runing == 0 ) {
        $tree->{status} = 'Running';
        $has_runing = 1;
    }
    return $has_runing;
}

